K-Means Clustering of Mall Customers
Johann Sebastian Catalla, BSCS-II
Professor: Dean Rodrigo Belleza Jr.As partial fulfillment for the course CSAL101: Algorithms and Complexity
About The Dataset¶
The dataset is sourced from kaggle. It includes various images flood images. The folder is composed of the actual images and the masks which represents the flood area.
# run once
#!pip install imantics --quiet
import os
import cv2
import json
import random
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import tensorflow as tf
import imantics
from PIL import Image
from skimage.transform import resize
from sklearn.model_selection import train_test_split
%matplotlib inline
Data Exploration and Preparation
The flood directory has two subdirectories: "Image" and "Mask". These subdirectories will hold the flood images and their associated masks that represent the flooded area.
base_dir = 'flood'
images_dir = f'{base_dir}/Image'
masks_dir = f'{base_dir}/Mask'
images_listdir = os.listdir(images_dir)
random_images = np.random.choice(images_listdir, size = 9, replace = False)
print(images_listdir[0:3])
['3059.jpg', '1049.jpg', '1080.jpg']
image_size=512
input_image_size=(512,512)
def read_image(path):
img = cv2.imread(path)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
img = cv2.resize(img, (image_size, image_size))
return img
Input images¶
I printed the sample images and their respective masks.
rows = 3
cols = 3
fig, ax = plt.subplots(rows, cols, figsize = (8,8))
for i, ax in enumerate(ax.flat):
if i < len(random_images):
img = read_image(f"{images_dir}/{random_images[i]}")
#print(img.shape)
ax.set_title(f"{random_images[i]}")
ax.imshow(img)
ax.axis('off')
Ground truth masks¶
fig, ax = plt.subplots(rows, cols, figsize = (8,8))
for i, ax in enumerate(ax.flat):
if i < len(random_images):
file=random_images[i][0:-4]+'.png'
img = read_image(f"{masks_dir}/{file}")
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ax.set_title(f"{random_images[i]}")
ax.imshow(img)
ax.axis('off')
I prepared the batches of flood images and their masks for further processing. I created two empty arrays: one for images and another for boolean masks.
The code reads the first 51 images and corresponding masks, resizes them if necessary, and stacks them into growing arrays. If there are errors reading a file, the code skips it and moves on to the next one.
MASKS=np.zeros((1,image_size, image_size, 1), dtype=bool)
IMAGES=np.zeros((1,image_size, image_size, 3),dtype=np.uint8)
for j,file in enumerate(images_listdir[0:51]): ##the smaller, the faster
try:
image = read_image(f"{images_dir}/{file}")
image_ex = np.expand_dims(image, axis=0)
IMAGES = np.vstack([IMAGES, image_ex])
file2=file[0:-4]+'.png'
mask = read_image(f"{masks_dir}/{file2}")
mask = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)
mask = mask.reshape(512,512,1)
mask_ex = np.expand_dims(mask, axis=0)
MASKS = np.vstack([MASKS, mask_ex])
except:
print(file)
continue
images=np.array(IMAGES)[1:51]
masks=np.array(MASKS)[1:51]
print(images.shape,masks.shape)
(50, 512, 512, 3) (50, 512, 512, 1)
The flood image and mask data are then splitted into training and testing sets. I utilized scikit-learn's train_test_split function.
Due to the limited number of images, I allocated 20% of the data for testing and the remaining 80% for training.
images_train, images_test, masks_train, masks_test = train_test_split(
images, masks, test_size=0.2, random_state=42)
Just ensuring the images train and masks train have the same size
print(len(images_train), len(masks_train))
40 40
U-Net¶
I defined the U-Net for flood image segmentation. I created:
- A conv_block that stacks two convolutional layers with intervening batch normalization and ReLU activation, designed to extract image features.
- An encoder_block that applies a conv_block followed by pooling to capture features, reduce image size, and create skip connections for preserving details.
- A decoder_block that upsamples features, combines them with corresponding skip connections, and applies a conv_block for further processing.
- The core Unet function builds the U-Net architecture. It takes the input image shape and builds an encoder part with several encoder_blocks to extract features and shrink the image size, while creating skip connections along the way. A bridge block with a conv_block is used at the lowest level. The decoder part uses multiple decoder_blocks to progressively increase the resolution of the features and combine them with skip connections for detailed recovery. Finally, a convolution with sigmoid activation is applied to create the flood segmentation mask.
def conv_block(input, num_filters):
conv = tf.keras.layers.Conv2D(num_filters, 3, padding="same")(input)
conv = tf.keras.layers.BatchNormalization()(conv)
conv = tf.keras.layers.Activation("relu")(conv)
conv = tf.keras.layers.Conv2D(num_filters, 3, padding="same")(conv)
conv = tf.keras.layers.BatchNormalization()(conv)
conv = tf.keras.layers.Activation("relu")(conv)
return conv
def encoder_block(input, num_filters):
skip = conv_block(input, num_filters)
pool = tf.keras.layers.MaxPool2D((2, 2))(skip)
return skip, pool
def decoder_block(input, skip, num_filters):
up_conv = tf.keras.layers.Conv2DTranspose(num_filters, (2, 2), strides=2, padding="same")(input)
conv = tf.keras.layers.Concatenate()([up_conv, skip])
conv = conv_block(conv, num_filters)
return conv
def Unet(input_shape):
inputs = tf.keras.layers.Input(input_shape)
skip1, pool1 = encoder_block(inputs, 64)
skip2, pool2 = encoder_block(pool1, 128)
skip3, pool3 = encoder_block(pool2, 256)
skip4, pool4 = encoder_block(pool3, 512)
bridge = conv_block(pool4, 1024)
decode1 = decoder_block(bridge, skip4, 512)
decode2 = decoder_block(decode1, skip3, 256)
decode3 = decoder_block(decode2, skip2, 128)
decode4 = decoder_block(decode3, skip1, 64)
outputs = tf.keras.layers.Conv2D(1, 1, padding="same", activation="sigmoid")(decode4)
model = tf.keras.models.Model(inputs, outputs, name="U-Net")
return model
unet_model = Unet((512,512,3))
unet_model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
unet_model.summary()
Model: "U-Net"
__________________________________________________________________________________________________
Layer (type) Output Shape Param # Connected to
==================================================================================================
input_1 (InputLayer) [(None, 512, 512, 3)] 0 []
conv2d (Conv2D) (None, 512, 512, 64) 1792 ['input_1[0][0]']
batch_normalization (Batch (None, 512, 512, 64) 256 ['conv2d[0][0]']
Normalization)
activation (Activation) (None, 512, 512, 64) 0 ['batch_normalization[0][0]']
conv2d_1 (Conv2D) (None, 512, 512, 64) 36928 ['activation[0][0]']
batch_normalization_1 (Bat (None, 512, 512, 64) 256 ['conv2d_1[0][0]']
chNormalization)
activation_1 (Activation) (None, 512, 512, 64) 0 ['batch_normalization_1[0][0]'
]
max_pooling2d (MaxPooling2 (None, 256, 256, 64) 0 ['activation_1[0][0]']
D)
conv2d_2 (Conv2D) (None, 256, 256, 128) 73856 ['max_pooling2d[0][0]']
batch_normalization_2 (Bat (None, 256, 256, 128) 512 ['conv2d_2[0][0]']
chNormalization)
activation_2 (Activation) (None, 256, 256, 128) 0 ['batch_normalization_2[0][0]'
]
conv2d_3 (Conv2D) (None, 256, 256, 128) 147584 ['activation_2[0][0]']
batch_normalization_3 (Bat (None, 256, 256, 128) 512 ['conv2d_3[0][0]']
chNormalization)
activation_3 (Activation) (None, 256, 256, 128) 0 ['batch_normalization_3[0][0]'
]
max_pooling2d_1 (MaxPoolin (None, 128, 128, 128) 0 ['activation_3[0][0]']
g2D)
conv2d_4 (Conv2D) (None, 128, 128, 256) 295168 ['max_pooling2d_1[0][0]']
batch_normalization_4 (Bat (None, 128, 128, 256) 1024 ['conv2d_4[0][0]']
chNormalization)
activation_4 (Activation) (None, 128, 128, 256) 0 ['batch_normalization_4[0][0]'
]
conv2d_5 (Conv2D) (None, 128, 128, 256) 590080 ['activation_4[0][0]']
batch_normalization_5 (Bat (None, 128, 128, 256) 1024 ['conv2d_5[0][0]']
chNormalization)
activation_5 (Activation) (None, 128, 128, 256) 0 ['batch_normalization_5[0][0]'
]
max_pooling2d_2 (MaxPoolin (None, 64, 64, 256) 0 ['activation_5[0][0]']
g2D)
conv2d_6 (Conv2D) (None, 64, 64, 512) 1180160 ['max_pooling2d_2[0][0]']
batch_normalization_6 (Bat (None, 64, 64, 512) 2048 ['conv2d_6[0][0]']
chNormalization)
activation_6 (Activation) (None, 64, 64, 512) 0 ['batch_normalization_6[0][0]'
]
conv2d_7 (Conv2D) (None, 64, 64, 512) 2359808 ['activation_6[0][0]']
batch_normalization_7 (Bat (None, 64, 64, 512) 2048 ['conv2d_7[0][0]']
chNormalization)
activation_7 (Activation) (None, 64, 64, 512) 0 ['batch_normalization_7[0][0]'
]
max_pooling2d_3 (MaxPoolin (None, 32, 32, 512) 0 ['activation_7[0][0]']
g2D)
conv2d_8 (Conv2D) (None, 32, 32, 1024) 4719616 ['max_pooling2d_3[0][0]']
batch_normalization_8 (Bat (None, 32, 32, 1024) 4096 ['conv2d_8[0][0]']
chNormalization)
activation_8 (Activation) (None, 32, 32, 1024) 0 ['batch_normalization_8[0][0]'
]
conv2d_9 (Conv2D) (None, 32, 32, 1024) 9438208 ['activation_8[0][0]']
batch_normalization_9 (Bat (None, 32, 32, 1024) 4096 ['conv2d_9[0][0]']
chNormalization)
activation_9 (Activation) (None, 32, 32, 1024) 0 ['batch_normalization_9[0][0]'
]
conv2d_transpose (Conv2DTr (None, 64, 64, 512) 2097664 ['activation_9[0][0]']
anspose)
concatenate (Concatenate) (None, 64, 64, 1024) 0 ['conv2d_transpose[0][0]',
'activation_7[0][0]']
conv2d_10 (Conv2D) (None, 64, 64, 512) 4719104 ['concatenate[0][0]']
batch_normalization_10 (Ba (None, 64, 64, 512) 2048 ['conv2d_10[0][0]']
tchNormalization)
activation_10 (Activation) (None, 64, 64, 512) 0 ['batch_normalization_10[0][0]
']
conv2d_11 (Conv2D) (None, 64, 64, 512) 2359808 ['activation_10[0][0]']
batch_normalization_11 (Ba (None, 64, 64, 512) 2048 ['conv2d_11[0][0]']
tchNormalization)
activation_11 (Activation) (None, 64, 64, 512) 0 ['batch_normalization_11[0][0]
']
conv2d_transpose_1 (Conv2D (None, 128, 128, 256) 524544 ['activation_11[0][0]']
Transpose)
concatenate_1 (Concatenate (None, 128, 128, 512) 0 ['conv2d_transpose_1[0][0]',
) 'activation_5[0][0]']
conv2d_12 (Conv2D) (None, 128, 128, 256) 1179904 ['concatenate_1[0][0]']
batch_normalization_12 (Ba (None, 128, 128, 256) 1024 ['conv2d_12[0][0]']
tchNormalization)
activation_12 (Activation) (None, 128, 128, 256) 0 ['batch_normalization_12[0][0]
']
conv2d_13 (Conv2D) (None, 128, 128, 256) 590080 ['activation_12[0][0]']
batch_normalization_13 (Ba (None, 128, 128, 256) 1024 ['conv2d_13[0][0]']
tchNormalization)
activation_13 (Activation) (None, 128, 128, 256) 0 ['batch_normalization_13[0][0]
']
conv2d_transpose_2 (Conv2D (None, 256, 256, 128) 131200 ['activation_13[0][0]']
Transpose)
concatenate_2 (Concatenate (None, 256, 256, 256) 0 ['conv2d_transpose_2[0][0]',
) 'activation_3[0][0]']
conv2d_14 (Conv2D) (None, 256, 256, 128) 295040 ['concatenate_2[0][0]']
batch_normalization_14 (Ba (None, 256, 256, 128) 512 ['conv2d_14[0][0]']
tchNormalization)
activation_14 (Activation) (None, 256, 256, 128) 0 ['batch_normalization_14[0][0]
']
conv2d_15 (Conv2D) (None, 256, 256, 128) 147584 ['activation_14[0][0]']
batch_normalization_15 (Ba (None, 256, 256, 128) 512 ['conv2d_15[0][0]']
tchNormalization)
activation_15 (Activation) (None, 256, 256, 128) 0 ['batch_normalization_15[0][0]
']
conv2d_transpose_3 (Conv2D (None, 512, 512, 64) 32832 ['activation_15[0][0]']
Transpose)
concatenate_3 (Concatenate (None, 512, 512, 128) 0 ['conv2d_transpose_3[0][0]',
) 'activation_1[0][0]']
conv2d_16 (Conv2D) (None, 512, 512, 64) 73792 ['concatenate_3[0][0]']
batch_normalization_16 (Ba (None, 512, 512, 64) 256 ['conv2d_16[0][0]']
tchNormalization)
activation_16 (Activation) (None, 512, 512, 64) 0 ['batch_normalization_16[0][0]
']
conv2d_17 (Conv2D) (None, 512, 512, 64) 36928 ['activation_16[0][0]']
batch_normalization_17 (Ba (None, 512, 512, 64) 256 ['conv2d_17[0][0]']
tchNormalization)
activation_17 (Activation) (None, 512, 512, 64) 0 ['batch_normalization_17[0][0]
']
conv2d_18 (Conv2D) (None, 512, 512, 1) 65 ['activation_17[0][0]']
==================================================================================================
Total params: 31055297 (118.47 MB)
Trainable params: 31043521 (118.42 MB)
Non-trainable params: 11776 (46.00 KB)
__________________________________________________________________________________________________
Train¶
I trained the U-Net flood segmentation model I built earlier. I fed the training flood images and their corresponding masks into the model's fit function.
To monitor the model's learning and prevent overfitting, I dedicated 20% of the training data for validation. I also trained the model in batches of 4 images and ran the training process for 40 epochs.
unet_result = unet_model.fit(
images_train, masks_train,
validation_split = 0.2, batch_size = 4, epochs = 40)
Epoch 1/40 8/8 [==============================] - 69s 1s/step - loss: -317.5229 - accuracy: 0.3060 - val_loss: -224273.7188 - val_accuracy: 0.1993 Epoch 2/40 8/8 [==============================] - 9s 1s/step - loss: -531.4960 - accuracy: 0.3952 - val_loss: 24078060.0000 - val_accuracy: 0.6145 Epoch 3/40 8/8 [==============================] - 8s 1s/step - loss: -640.2673 - accuracy: 0.4209 - val_loss: 13830448.0000 - val_accuracy: 0.6145 Epoch 4/40 8/8 [==============================] - 8s 1s/step - loss: -657.3428 - accuracy: 0.3698 - val_loss: 26745482.0000 - val_accuracy: 0.6145 Epoch 5/40 8/8 [==============================] - 9s 1s/step - loss: -731.2119 - accuracy: 0.3232 - val_loss: 19639602.0000 - val_accuracy: 0.6145 Epoch 6/40 8/8 [==============================] - 8s 1s/step - loss: -777.8339 - accuracy: 0.2761 - val_loss: 6624048.0000 - val_accuracy: 0.6145 Epoch 7/40 8/8 [==============================] - 8s 1s/step - loss: -883.8488 - accuracy: 0.2718 - val_loss: 248768.1562 - val_accuracy: 0.6145 Epoch 8/40 8/8 [==============================] - 8s 1s/step - loss: -903.3406 - accuracy: 0.2562 - val_loss: 2772233.0000 - val_accuracy: 0.6145 Epoch 9/40 8/8 [==============================] - 8s 1s/step - loss: -981.9388 - accuracy: 0.2353 - val_loss: 1954045.3750 - val_accuracy: 0.6145 Epoch 10/40 8/8 [==============================] - 8s 1s/step - loss: -1091.8596 - accuracy: 0.2073 - val_loss: 283093.3750 - val_accuracy: 0.6145 Epoch 11/40 8/8 [==============================] - 8s 1s/step - loss: -1085.4352 - accuracy: 0.1956 - val_loss: 315885.9062 - val_accuracy: 0.6145 Epoch 12/40 8/8 [==============================] - 8s 1s/step - loss: -1198.9594 - accuracy: 0.1802 - val_loss: 236124.2812 - val_accuracy: 0.6145 Epoch 13/40 8/8 [==============================] - 8s 1s/step - loss: -1242.6714 - accuracy: 0.1597 - val_loss: 240205.2500 - val_accuracy: 0.6145 Epoch 14/40 8/8 [==============================] - 8s 1s/step - loss: -1300.0221 - accuracy: 0.1709 - val_loss: 3917.5037 - val_accuracy: 0.6145 Epoch 15/40 8/8 [==============================] - 8s 1s/step - loss: -1396.0645 - accuracy: 0.1562 - val_loss: 2392.7568 - val_accuracy: 0.6144 Epoch 16/40 8/8 [==============================] - 8s 1s/step - loss: -1467.0767 - accuracy: 0.1672 - val_loss: 1757.9507 - val_accuracy: 0.6145 Epoch 17/40 8/8 [==============================] - 8s 1s/step - loss: -1547.5094 - accuracy: 0.1540 - val_loss: 1732.1936 - val_accuracy: 0.6145 Epoch 18/40 8/8 [==============================] - 8s 1s/step - loss: -1613.5913 - accuracy: 0.1581 - val_loss: 3715.2363 - val_accuracy: 0.6145 Epoch 19/40 8/8 [==============================] - 9s 1s/step - loss: -1693.0769 - accuracy: 0.1835 - val_loss: 1269.3572 - val_accuracy: 0.6140 Epoch 20/40 8/8 [==============================] - 8s 1s/step - loss: -1781.1072 - accuracy: 0.1847 - val_loss: 1794.1149 - val_accuracy: 0.6142 Epoch 21/40 8/8 [==============================] - 9s 1s/step - loss: -1856.1777 - accuracy: 0.1681 - val_loss: 904.4265 - val_accuracy: 0.6027 Epoch 22/40 8/8 [==============================] - 9s 1s/step - loss: -1999.6028 - accuracy: 0.1650 - val_loss: 299.8980 - val_accuracy: 0.5474 Epoch 23/40 8/8 [==============================] - 8s 1s/step - loss: -2046.9307 - accuracy: 0.1603 - val_loss: 1699.7529 - val_accuracy: 0.5603 Epoch 24/40 8/8 [==============================] - 8s 1s/step - loss: -2173.3223 - accuracy: 0.1588 - val_loss: 6372.9893 - val_accuracy: 0.6128 Epoch 25/40 8/8 [==============================] - 8s 1s/step - loss: -2274.5242 - accuracy: 0.1782 - val_loss: 4972.4424 - val_accuracy: 0.6133 Epoch 26/40 8/8 [==============================] - 8s 1s/step - loss: -2344.7363 - accuracy: 0.1799 - val_loss: 1141.7584 - val_accuracy: 0.6005 Epoch 27/40 8/8 [==============================] - 9s 1s/step - loss: -2505.2192 - accuracy: 0.1787 - val_loss: 460.8345 - val_accuracy: 0.6080 Epoch 28/40 8/8 [==============================] - 8s 1s/step - loss: -2556.1479 - accuracy: 0.1993 - val_loss: 1334.6827 - val_accuracy: 0.6123 Epoch 29/40 8/8 [==============================] - 8s 1s/step - loss: -2741.0308 - accuracy: 0.1731 - val_loss: 36.2159 - val_accuracy: 0.6046 Epoch 30/40 8/8 [==============================] - 8s 1s/step - loss: -2761.2593 - accuracy: 0.1664 - val_loss: -937.1934 - val_accuracy: 0.1831 Epoch 31/40 8/8 [==============================] - 8s 1s/step - loss: -2859.8491 - accuracy: 0.1541 - val_loss: 34.2534 - val_accuracy: 0.4544 Epoch 32/40 8/8 [==============================] - 9s 1s/step - loss: -3016.6003 - accuracy: 0.1442 - val_loss: -101.6258 - val_accuracy: 0.2162 Epoch 33/40 8/8 [==============================] - 8s 1s/step - loss: -3206.3298 - accuracy: 0.1309 - val_loss: 824.6451 - val_accuracy: 0.5821 Epoch 34/40 8/8 [==============================] - 8s 1s/step - loss: -3226.0547 - accuracy: 0.1123 - val_loss: 862.4459 - val_accuracy: 0.5778 Epoch 35/40 8/8 [==============================] - 9s 1s/step - loss: -3415.1719 - accuracy: 0.1251 - val_loss: -1224.5474 - val_accuracy: 0.1952 Epoch 36/40 8/8 [==============================] - 8s 1s/step - loss: -3552.5605 - accuracy: 0.1531 - val_loss: 1542.0995 - val_accuracy: 0.5950 Epoch 37/40 8/8 [==============================] - 9s 1s/step - loss: -3715.8950 - accuracy: 0.1308 - val_loss: 197.9382 - val_accuracy: 0.6026 Epoch 38/40 8/8 [==============================] - 9s 1s/step - loss: -3834.2515 - accuracy: 0.1355 - val_loss: -118.9549 - val_accuracy: 0.5089 Epoch 39/40 8/8 [==============================] - 8s 1s/step - loss: -3955.6641 - accuracy: 0.1356 - val_loss: 2189.6885 - val_accuracy: 0.6086 Epoch 40/40 8/8 [==============================] - 9s 1s/step - loss: -4128.2051 - accuracy: 0.1345 - val_loss: -804.7332 - val_accuracy: 0.2587
Predict¶
unet_predict = unet_model.predict(images_test)
1/1 [==============================] - 30s 30s/step
I created four sets of predictions from the U-Net model's output (unet_predict) on the test images. Here's what I did for each:
unet_predict1: I used a threshold of 0.1. The code creates a new variable unet_predict1 by comparing each value in unet_predict to 0.1. If the value in unet_predict is greater than 0.1, it gets assigned a 1 (indicating likely flood pixels). Otherwise, it gets assigned a 0. Finally, I convert the resulting array to the uint8 data type.
unet_predict2, unet_predict3, unet_predict4: I repeated the same process for unet_predict2, unet_predict3, and unet_predict4 but with different thresholds (0.2, 0.3, and 0.4 respectively). This essentially creates four potential flood segmentation masks with varying strictness. A higher threshold means only pixels with a higher probability of being flood will be included in the mask.
unet_predict1 = (unet_predict > 0.1).astype(np.uint8)
unet_predict2 = (unet_predict > 0.2).astype(np.uint8)
unet_predict3 = (unet_predict > 0.3).astype(np.uint8)
unet_predict4 = (unet_predict > 0.4).astype(np.uint8)
len(unet_predict)
10
I defined a function called show_result to help me analyze the flood segmentation predictions from my U-Net model. This function lets me visually compare the original flood image, the model's prediction for that image, and the ground truth mask (the actual flood mask) all side-by-side.
The function creates a figure with three subplots.
In the first subplot, I display the original flood image with the title "Original."
In the second subplot, I display the prediction from my U-Net model for that image. I also include a title in this subplot that specifies the threshold value I used to create the prediction (e.g., "U-Net: p>0.2").
Finally, in the third subplot, I display the ground truth mask, which represents the actual flooded areas in the image. This subplot is titled "Ground Truth." By turning off the axis labels in each subplot, I create a clean visualization that allows me to focus on the image content.
def show_result(og, unet, target, p):
fig, axs = plt.subplots(1, 3, figsize=(12,12))
axs[0].set_title("Original")
axs[0].imshow(og)
axs[0].axis('off')
axs[1].set_title("U-Net: p>"+str(p))
axs[1].imshow(unet)
axs[1].axis('off')
axs[2].set_title("Ground Truth")
axs[2].imshow(target)
axs[2].axis('off')
plt.show()
show_test_idx = random.sample(range(len(unet_predict)), 3)
for idx in show_test_idx:
show_result(images_test[idx], unet_predict1[idx], masks_test[idx], 0.1)
show_result(images_test[idx], unet_predict2[idx], masks_test[idx], 0.2)
show_result(images_test[idx], unet_predict3[idx], masks_test[idx], 0.3)
show_result(images_test[idx], unet_predict4[idx], masks_test[idx], 0.4)
In the first subplot, I display the original flood image with the title "Original."
In the second subplot, I display the prediction from my U-Net model for that image. I also include a title in this subplot that specifies the threshold value I used to create the prediction (e.g., "U-Net: p>0.2").
Finally, in the third subplot, I display the ground truth mask, which represents the actual flooded areas in the image. This subplot is titled "Ground Truth." By turning off the axis labels in each subplot, I create a clean visualization that allows me to focus on the image content.
In these plots we can see the 4 thresholds as well as the input and output of the model, and ground truth. By observing these results, we can say that the higher threshold has a more accurate prediction of the flood area.